#!/usr/bin/perl

#Copyright (C) 2009  Tiziano Fogli, Simone Bernini

#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# fogli@cs.unibo.it
# sbernini@cs.unibo.it

#use strict;
use warnings;
use diagnostics;
use XML::LibXML;
use XML::XPath;
use XML::XPath::XMLParser;

use FileScalarPeer;
use FileScalar;

$configFile = "config.xml";
$configFileXsd = "FileScalarConfig.xsd";

$xpath = undef;

$risDir = `pwd`;
$datDir = "./dat";


MAIN: {
	    #parametri in input
	    my ($newConfigFile) = @ARGV;
	
	    #sovrascrivo la dir dei risultati se questa è stata passata in input
        $configFile = $newConfigFile if defined($newConfigFile);

        ### VALIDAZIONE FILE DI CONFIG
        my $xmlParser = XML::LibXML->new();
        my $configFileDom = $xmlParser->parse_file($configFile); 
        
        my $validator = XML::LibXML::Schema->new(location => $configFileXsd);

        eval { $validator->validate($configFileDom) };
        die $@ if $@; 

        ### RECUPERO DAL FILE CONFIG I PARAMETRI GLOBALI
        $xpath = XML::XPath->new(filename => $configFile);
        $risDir = $xpath->getNodeText('//risdir/@dir');


        my $fsp = new FileScalarPeer($risDir);


        ### RECUPERO TUTTI I GRAFICI CON INFO ANNESSE

        my $graphics = $xpath->find('//graphic');
        my $gcounter = 0;
        foreach $graphic ($graphics->get_nodelist()) {

            $gcounter++;
            print "$gcounter -> ";

            my $nodePath = XML::XPath->new(context => $graphic);

            my $title = $nodePath->getNodeText('title/head/@name');

            my $xtitle = $nodePath->getNodeText('title/x/@name');
            my $xtics = "autofreq";
            $xtics = $nodePath->find('title/x/@ticsoffset')->get_node(1)->string_value() if($nodePath->find('title/x/@ticsoffset')->size() >= 1);

            my $ytitle = $nodePath->getNodeText('title/y/@name');
            my $ytics = "autofreq";
            $ytics = $nodePath->find('title/y/@ticsoffset')->get_node(1)->string_value() if($nodePath->find('title/y/@ticsoffset')->size() >= 1);

            my $ztitle = $nodePath->getNodeText('title/z/@name');
            my $ztics = "autofreq"; 
            if($ztitle eq "") {
                undef $ztitle;
                undef $ztics; 
            } else {
                $ztics = "autofreq";
                $ztics = $nodePath->getNodeText('title/z/@ticsoffset') if($nodePath->find('title/z/@ticsoffset')->size() >= 1);
            }

            my $yregex = $nodePath->getNodeText('title/y/@regex');
            my $groupBy = $nodePath->getNodeText('values/group/@by');
            my $orderBy = $nodePath->getNodeText('values/order/@by');
            my $xSupLimit;
            my $xSupLimitNodeSet = $nodePath->find('values/order/@suplimit');
            if($xSupLimitNodeSet->size() == 0) {
                undef $xSupLimit;
            } else { 
                $xSupLimit = $xSupLimitNodeSet->get_node(1)->string_value();
            }
            my $zBy = $nodePath->getNodeText('title/z/@paramname'); 
            if($zBy eq "") {
                undef $zBy;
            }
            #my $faith = $nodePath->getNodeText('values/faith/@value');
            my $faith = $nodePath->find('values/faith/@value');
            my $xproportion = ${$nodePath->getNodeText('values/proportions/@x')};
            my $yproportion = ${$nodePath->getNodeText('values/proportions/@y')};

            my %filters;
            my $filtersNode = $nodePath->find('filters/filter');
            foreach $filter ($filtersNode->get_nodelist()) {
                my $filterOnParam = $filter->find('@onParam', $filter)->string_value();
                my $filterWithValue = $filter->find('@withValue', $filter)->string_value();
                $filters{$filterOnParam} = $filterWithValue;
            }

            my %res = $fsp->getSortedGroupedFilesScalar($orderBy,$groupBy, $zBy, %filters);
       
            $title = computeGraphicTitle($gcounter, $title, %filters);

            plot($risDir, $title, $xtitle, $xtics, $ytitle, $ytics, $ztitle, $ztics, $groupBy, $orderBy, $xSupLimit, $faith->get_node(1)->string_value(), $xproportion, $yproportion, $yregex, $zBy, %res);
            
            undef $ztitle;
            undef $xSupLimit;
            undef $zBy;
        }

}

sub plot {
    my $outDir = shift;
    my $title = shift;
    my $xtitle = shift;
    my $xtics = shift;
    my $ytitle = shift;
    my $ytics = shift;
    my $ztitle = shift;
    my $ztics = shift;
    my $groupBy = shift;
    my $orderBy = shift;
    my $xSupLimit = shift;
    my $faith = shift;
    my $xproportion = shift;
    my $yproportion = shift;
    my $yregex = shift;
    my $zBy = shift;

    my (%functions) = @_;
    
    my @X = @{$functions{"x"}}; delete($functions{"x"});
    my $lx = @X;
    ($lx > 0) or die("no suitable data for this graphic\n");

    if(not(defined($xSupLimit))) {
        $xSupLimit = $X[-1];
    }

    `rm -Rf $datDir`;
    `mkdir $datDir`; 

    my @functionsToPlot;
    $gnuplotCmd = "plot";
            
    my $param = $yregex; $param =~ s/\s+/_/g;
    my $regex = qr/$param.\s+(\d+\.?\d*(e(\+|-)\d+)?)/; 
    my $fileName = "";
    my $lc = 0;
    if(not(defined($zBy))) { 
        foreach $f (sort {$a <=> $b} keys(%functions)) {
	        $fileName = "$datDir/$f.dat";
        
            #my $param = $yregex; $param =~ s/\s+/_/g;
            #my $regex = qr/$param.\s+(\d+\.?\d*(e(\+|-)\d+)?)/; 

            open(FFILE, ">$fileName");
            
            my $xindex = 0;
	        foreach $fs (@{$functions{$f}}) {
                    my @average = $fs->calculateAverage(sub { $_[0] =~ /$regex/; return $1}, undef, $faith, $yproportion);
                    if((defined($X[$xindex])) && ($X[$xindex] <= $xSupLimit)) {
                        my $xprop = $X[$xindex++] * $xproportion;
                        print FFILE "$xprop\t$average[1]\t$average[0]\t$average[2]\n";
                    }   else { $xindex++; }
	        }
	        close(FFILE);
            
            computePercentage($title, $xtitle, $ytitle, $ztitle, "$f.dat", "$groupBy$f");

	        push(@functionsToPlot, " \"$fileName\" title \"$groupBy$f\" with lines lc $lc, \"$fileName\" using 1:2:3:4 notitle w yerrorbars lt 1 lc $lc");
            $lc++;
        }
    } else {
        $gnuplotCmd = "splot";
        foreach $f (sort {$a <=> $b} keys(%functions)) {
	        $fileName = "$datDir/$f.dat";
        
            open(FFILE, ">$fileName");

	
            foreach my $x (@X) {
                if($x <= $xSupLimit) {
                    foreach my $fs (@{$functions{$f}}) {
                        if($x == $fs->getParameter($orderBy)) {
                            my $xprop = $x * $xproportion;
                            my @average = $fs->calculateAverage(sub { $_[0] =~ /$regex/; return $1}, undef, $faith, $yproportion);
                            my $z = $fs->getParameter($zBy);
                            print FFILE "$xprop\t$average[1]\t$average[0]\t$average[2]\t$z\n";
                        }
                    }
                    print FFILE "\n";
                }
            }

	        close(FFILE);
            
            computePercentage($title, $xtitle, $ytitle, $ztitle, "$f.dat", "$groupBy$f");

	        push(@functionsToPlot, " \"$fileName\" using 1:5:2 title \"$groupBy$f\" with linesp");
        }
    }

	$gnuplotCmd = $gnuplotCmd.join(',',@functionsToPlot);

    my $lastX = (($xSupLimit * $xproportion) / 100) * 110;

    ### INVIO IN PIPE IL COMANDO A GNUPLOT
    #$gnuplotCmd era prima di set terminal e il comando replot era non commemntato
    if(not(defined($zBy))) {
    open(GNUPLOT, "|gnuplot");
    print GNUPLOT <<EOPLOT;
    set grid
    set autoscale
    set xrange [*:$lastX]
    set title "$title"
    set xlabel "$xtitle"
    set xtics $xtics
    set ylabel "$ytitle"
    set ytics $ytics
    set key right bottom
    set terminal postscript eps enhanced dashed color
	set output "tmp.eps"
	$gnuplotCmd
    #replot
EOPLOT
    close(GNUPLOT);
    } else {
    open(GNUPLOT, "|gnuplot");
    print GNUPLOT <<EOPLOT3D;
    set grid
    #set hidden3d
    #set pm3d
    set xrange [*:$lastX]
    set title "$title"
    set xlabel "$xtitle"
    set xtics $xtics
    set ylabel "$ztitle" rotate by 90 
    set ytics $ztics
    set zlabel "$ytitle" rotate by 90
    set ztics $ytics
    set key right top
    set view 57,35
    set autoscale
    set terminal postscript eps enhanced dashed color
    set output "tmp.eps"
    $gnuplotCmd
    #replot
EOPLOT3D
    close(GNUPLOT);
    }

    $gnuplotCmd =~ s/\.\/dat\///g;
    my $scriptTitle = $title;
    $scriptTitle =~ s/\s+/_/g;


    if(not(defined($zBy))) {
    open(SCRIPT, ">tmp.gpt");
    print SCRIPT <<EOSCRIPT;
    set grid
    set autoscale
    set xrange [0:$lastX]
    set title "$title"
    set xlabel "$xtitle"
    set xtics $xtics
    set ylabel "$ytitle"
    set ytics $ytics
    set key right bottom
	$gnuplotCmd
    #set terminal postscript eps enhanced dashed color
    #set output "replotted.eps"
    #replot
EOSCRIPT
    close(SCRIPT);
    } else {
    open(SCRIPT, ">tmp.gpt");
    print SCRIPT <<EOSCRIPT3D;
    set grid
    #set hidden3d
    #set pm3d
    set xrange [0:$lastX]
    set title "$title"
    set xlabel "$xtitle"
    set xtics $xtics
    set ylabel "$ztitle" rotate by 90 
    set ytics $ztics
    set zlabel "$ytitle" rotate by 90
    set ztics $ytics
    set key right top 
    set view 57,35
    set autoscale
	$gnuplotCmd
    #set terminal postscript eps enhanced dashed color
    #set output "replotted.eps"
    #replot
EOSCRIPT3D
    close(SCRIPT);
    }

        
    ## RIMUOVO GLI SPAZI DAL TITOLO PER IL NOME DEL FILE
    $title =~ s/\s+/_/g;
        
    ### CALCOLO IL NOME DI OUTPUT DEL GRAFICO
    #my $date = `date +%F_%H-%M`;
    #$date = "_$date";
    #chomp($date);
    my $outputGraphName = "$title.eps";
    
    ### SPOSTO I FILE TEMPORANEI NELLA DIR DI DESTINAZIONE
    my $outputDirName = "graphics/$title"; 
    #my $outputDirName = "$outDir/$title";
    `mv dat $outputDirName`;
    `epstopdf tmp.eps`;
    `mv tmp.eps $outputDirName/$outputGraphName`;

    $outputGraphName =~ s/\.eps/\.pdf/g;
    `mv tmp.pdf $outputDirName/$outputGraphName`;
    `mv tmp.gpt $outputDirName/$title.gpt`;


}

sub computeGraphicTitle {
    my ($gcounter, $title, %filters) = @_;
    
    my @subtitle;
    foreach $param (keys(%filters)) {
        push(@subtitle, "$param $filters{$param}");
    }

    $title = $title."\\n".join(', ',@subtitle);

    $title =~ s/inj/off load/g;
    $title =~ s/traffic\s+3/dvopd/g;
    $title =~ s/traffic\s+2/vopd/g;
    $title =~ s/traffic\s+1/mwd/g;
    $title =~ s/traffic\s+0/uniform/g;
    $title =~ s/scenario\s+0/ppack/g;
    $title =~ s/scenario\s+1/pack vnet/g;
    $title =~ s/scenario\s+2/pack net/g;
    $title =~ s/consrate/cons rate /g;
    $title =~ s/amc/input conn /g;
    $title =~ s/btb/max btb conn /g;
    $title =~ s/nivc/vc on ni port /g;


    print "\t$title\n";
    return $title;
}

sub computePercentage {
    my ($title, $xtitle, $ytitle, $ztitle, $fileToCompareName, $fname) = @_;

    my $datBaseFileName = `ls $datDir | grep '^[0-9]\.dat' | sort -n | head -n 1`;
    chomp($datBaseFileName);
    
    if($datBaseFileName eq $fileToCompareName) {
        return 0;
    }

    my @datBase = ();
    open(DAT, "$datDir/$datBaseFileName");
        @datBase = <DAT>;
    close(DAT);

    my @datToCompare = ();
    open(DAT, "$datDir/$fileToCompareName");
        @datToCompare = <DAT>;
    close(DAT);

    my $gnuplotCmd;
    my @percentualToPlot;
    my @percentage = ();
    my $percentageRow = "";
    my $rowNo = 0;
    foreach my $row (@datToCompare) {
        @percentage = ();
        $percentageRow = "";

        my @baseRow = split(' ', $datBase[$rowNo]);
        my @datRow = split(' ', $row);
        
        if ($baseRow[0]) {
            push(@percentage, $baseRow[0]);
            push(@percentage, sprintf('%.2f',(($datRow[1] / $baseRow[1])*100)-100));
            if (@baseRow == 5) {
                push(@percentage, $baseRow[4]);
            }
            $percentageRow = join(' ', @percentage);
            `echo $percentageRow >> perc_$fname.dat`;
        } else {
            `echo "" >> perc_$fname.dat`;
        }
        
        $rowNo++;
    }

    `mv perc_$fname.dat $datDir/perc_$fname.dat`;

    my @zrevel = split(' ', $datBase[0]);
    if(@zrevel < 5) {
        $gnuplotCmd = "plot";
        push(@percentualToPlot, " \"$datDir/perc_$fname.dat\" title \"$fname\" with boxes, \"$datDir/perc_$fname.dat\" using 1:2:2 notitle with labels font \"arial,12\" left point pt 0 offset -2,1");
    } else {
        $gnuplotCmd = "splot";
        push(@percentualToPlot, " \"$datDir/perc_$fname.dat\" using 1:3:2 title \"$fname\" with linesp, \"$datDir/perc_$fname.dat\" using 1:3:2:2 notitle with labels font \"arial,7\" left point pt 0 offset -1,1");
    }
     

    if($gnuplotCmd eq "plot") {
    $gnuplotCmd = $gnuplotCmd.join(',', @percentualToPlot);
open(GNUPLOT, "|gnuplot");
    print GNUPLOT <<EOPERCPLOT;
    set grid
    set autoscale
    set title "Percentual Gain on $title"
    set xlabel "$xtitle"
    set ylabel "% $ytitle"
    set key right bottom
    set terminal postscript eps enhanced solid color
    set output "perc_$fname.eps"
    $gnuplotCmd
    #replot
EOPERCPLOT
close(GNUPLOT);
    } else {
    $gnuplotCmd = $gnuplotCmd.join(',', @percentualToPlot);
    open(GNUPLOT, "|gnuplot");
    print GNUPLOT <<EOPERCPLOT3D;
    set grid
    #set hidden3d
    #set surface
    #set pm3d at sb
    set title "Percentual Gain on $title"
    set xlabel "$xtitle"
    set ylabel "$ztitle"
    set zlabel "% $ytitle" rotate by 90
    set key right top
    set view 57,35
    set autoscale
    set terminal postscript eps enhanced solid color
    set output "perc_$fname.eps"
    $gnuplotCmd
    #replot
EOPERCPLOT3D
    close(GNUPLOT);
    }

    `mv perc_$fname.eps dat`;
}
